/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlForeignKey;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.ColToTableRev;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;
import uk.ac.ic.doc.automed.reps.SchemaObject;

import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;

/**
 * UI for the table to column transformation pattern
 * 
 * @author Tudor Dobrila
 * 
 */
public class ColToTableRevDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = -8317121760372314661L;
	private static final Logger LOGGER = Logger
			.getLogger("ro.dta.idbi.ui.patterns.ColToTableRevDialog");

	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		if (objects.length != 3) {
			return false;
		}

		ISqlForeignKey fKey = null;
		ISqlTable tbl = null;
		ISqlColumn col = null;

		// Expect 1 column, 1 table and 1 foreign key
		for (Object o : objects) {
			if (o instanceof ISqlForeignKey) {
				fKey = (ISqlForeignKey) o;
			} else if (o instanceof ISqlColumn) {
				col = (ISqlColumn) o;
			} else if (o instanceof ISqlTable) {
				tbl = (ISqlTable) o;
			}
		}
		if (fKey == null || tbl == null || col == null) {
			return false;
		}

		final ISqlTable origTbl = (ISqlTable) col.getParent();
		// Verify source and target
		if (fKey.getSource() != origTbl) {
			return false;
		}
		if (fKey.getTarget() != tbl) {
			return false;
		}

		// Check if there is only one column in the target table, which must be
		// PK, so the table can be removed
		final List<ISqlColumn> cols = tbl.getColumns();
		if (cols.size() != 1 || !cols.get(0).isPK()) {
			return false;
		}

		// Check that the FK is between the selected tables
		final SchemaObject obj = fKey.getSchemaObject();
		final Object[] fkDef = obj.getSchemeDefinition();
		if (fkDef.length != 5) {
			return false;
		}
		final SchemaObject source = (SchemaObject) fkDef[1];
		final SchemaObject sourceCol = (SchemaObject) fkDef[2];
		final SchemaObject target = (SchemaObject) fkDef[3];
		final SchemaObject targetCol = (SchemaObject) fkDef[4];
		if (!source.getValue().equals(origTbl.getValue())
				|| !target.getValue().equals(tbl.getValue())) {
			return false;
		}
		if (!sourceCol.getValue().equals(col.getValue())
				|| !targetCol.getValue().equals(cols.get(0).getValue())) {
			return false;
		}

		return true;
	}

	private transient ISqlForeignKey foreignKey;
	private transient ISqlColumn col;
	private transient ISqlTable tbl;

	/**
	 * Table to column dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public ColToTableRevDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"Transformation pattern can't be applied to given elements");
		}

		final ISqlTable origTbl = (ISqlTable) col.getParent();
		macroField.setText(ColToTableRev.COMMAND + " (<<" + origTbl.getValue() + ","
				+ col.getValue() + ">>, <<" + tbl.getValue() + ">>)");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.patterns.TransformationDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			getElements();

			final SqlTableCell newTbl = (SqlTableCell) tbl.clone();
			newTbl.setStyle("SEL_TABLE");
			newTbl.layout(graph);

			final SqlTableCell origTbl = (SqlTableCell) col.getParent().clone();
			final ISqlColumn column = origTbl.getColumn(col.getValue().toString());
			column.setStyle("SEL_COL");
			origTbl.layout(graph);

			final SqlForeignKeyCell fk1 = new SqlForeignKeyCell(null, null, "", foreignKey.getValue()
					.toString(), origTbl, newTbl, "FK_EDGE");
			graph.addCell(fk1, graph.getDefaultParent());
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			final mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
			layout.execute(graph.getDefaultParent());
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.TransformationDialog#createTransformedDiagram()
	 */
	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			getElements();

			final SqlTableCell origTbl = (SqlTableCell) col.getParent().clone();
			origTbl.setGeometry(new mxGeometry(5, 5, origTbl.getGeometry().getWidth(), origTbl
					.getGeometry().getHeight()));
			origTbl.layout(graph);
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	private void getElements() {
		// Expect 1 column, 1 table and one foreign key
		for (ISqlConstruct e : element) {
			if (e instanceof ISqlForeignKey) {
				foreignKey = (ISqlForeignKey) e;
			} else if (e instanceof ISqlColumn) {
				col = (ISqlColumn) e;
			} else if (e instanceof ISqlTable) {
				tbl = (ISqlTable) e;
			}
		}
	}

}
